/*
 * @Author: xiaosihan 
 * @Date: 2023-12-02 23:19:15 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-03-07 16:07:14
 */

import utils from "@utils";
import axios from "axios";
import { uniq } from "lodash";
import { autorun, toJS } from "mobx";
import { AmbientLight, Color, DirectionalLight, Group, MeshBasicMaterial, MeshLambertMaterial, PointLight, Vector3 } from "three";
import ThreeBase from "three-base/ThreeBase";
import threeUtils from "three-base/threeUtils";
import { degToRad, generateUUID } from "three/src/math/MathUtils";
import Ground from "./component3d/Ground/Ground";
import LabelGroup from "./component3d/LabelGroup/LabelGroup";
import MeshBase from "./component3d/MeshBase/MeshBase";
import modelGroup from "./component3d/ModelGroup/ModelGroup";
import MonitorGroup from "./component3d/MonitorGroup/MonitorGroup";
import Sky from "./component3d/Sky/Sky";
import editor3DStore from "./editor3DStore";
import floorMeshGroup from "./component3d/FloorMeshGroup/FloorMeshGroup";
import InfoModal from "./component/InfoModal/InfoModal";

// 3d编辑器
class Editor3D extends ThreeBase {

    constructor() {
        super();
    }

    async ck(url: string) {
        var result = false;
        await axios.post(url)
            .then((res: any) => {
                if (null != res && null != res.data && res.data.isOk == true && res.data.data == true)
                    result = true;
                else
                    result = false;
            })
        return result;
    }
    // 环境光
    ambientLight = (() => {
        const ambientLight = new AmbientLight("#fff", 2);
        this.scene.add(ambientLight);
        return ambientLight;
    })();

    //点光源
    pointLight = (() => {
        const pointLight = new PointLight("#fff", 2);
        pointLight.distance = 1000;
        pointLight.decay = 0;
        pointLight.position.set(50, 40, 50);
        this.scene.add(pointLight);
        return pointLight;
    })();

    // 方向光
    // directionalLight = (() => {
    //     const directionalLight = new DirectionalLight("#fff", 2);
    //     directionalLight.position.set(-10, 10, 5);
    //     directionalLight.target.position.set(0, 0, 0);
    //     directionalLight.castShadow = true;
    //     directionalLight.shadow.mapSize.set(1024, 1024);
    //     this.scene.add(directionalLight);
    //     return directionalLight;
    // })();

    // 许可
    license?: string = "";

    init() {
        super.init();
        this.renderer.domElement.style.backgroundColor = "#000000";

        this.controls.maxPolarAngle = degToRad(85);
        this.controls.screenSpacePanning = false;

        this.resetLookAt({
            position: new Vector3(100, 100, 100),
            center: new Vector3(0, 0, 0)
        });

    }

    //视频弹窗dom
    videoModal = (() => {
        const videoModal = document.createElement("div");
        videoModal.dataset.name = ""; // max
        videoModal.style.position = "absolute";
        videoModal.style.left = "100%";
        videoModal.style.top = "0px";
        videoModal.style.width = "300px";
        videoModal.style.border = "1px solid #1ACAD8";
        videoModal.style.borderRadius = "8px";
        videoModal.style.backgroundColor = "#313C4D";
        videoModal.style.padding = "4px 8px";
        videoModal.style.boxSizing = "border-box";
        return videoModal;
    })();

    //视频标题
    videoTitle = (() => {
        const videoTitle = document.createElement("p");
        videoTitle.innerText = "视频标题";
        videoTitle.style.color = "#ffffff";
        this.videoModal.appendChild(videoTitle);
        return videoTitle;
    })();

    //信息弹窗 
    infoModal = new InfoModal();

    //最大化按钮
    maxBtn = (() => {
        const maxBtn = document.createElement("span");
        maxBtn.innerText = "□";
        maxBtn.style.display = "inline-block";
        maxBtn.style.border = "1px solid rgb(26, 202, 216)";
        maxBtn.style.width = "20px";
        maxBtn.style.height = "20px";
        maxBtn.style.lineHeight = "20px";
        maxBtn.style.fontSize = "12px";
        maxBtn.style.borderRadius = "4px";
        maxBtn.style.color = "rgb(26, 202, 216)";
        maxBtn.style.position = "absolute";
        maxBtn.style.right = "28px";
        maxBtn.style.top = "4px";
        maxBtn.style.cursor = "pointer";
        maxBtn.style.visibility = "visible";
        maxBtn.onclick = () => {
            this.maxBtn.style.visibility = "hidden";
            this.minBtn.style.visibility = "visible";

            this.videoModal.style.width = "100%";
            this.videoModal.style.height = "100%";
            this.videoModal.dataset.name = "max";
        }
        this.videoModal.appendChild(maxBtn);

        return maxBtn;
    })();

    //最小化按钮
    minBtn = (() => {
        const minBtn = document.createElement("span");
        minBtn.innerText = "_";
        minBtn.style.display = "inline-block";
        minBtn.style.border = "1px solid rgb(26, 202, 216)";
        minBtn.style.width = "20px";
        minBtn.style.height = "20px";
        minBtn.style.lineHeight = "20px";
        minBtn.style.fontSize = "12px";
        minBtn.style.borderRadius = "4px";
        minBtn.style.color = "rgb(26, 202, 216)";
        minBtn.style.position = "absolute";
        minBtn.style.right = "28px";
        minBtn.style.top = "4px";
        minBtn.style.cursor = "pointer";
        minBtn.style.visibility = "hidden";
        minBtn.onclick = () => {
            this.minBtn.style.visibility = "hidden";
            this.maxBtn.style.visibility = "visible";
            this.videoModal.style.width = "300px";
            this.videoModal.style.height = "unset";
            this.videoModal.dataset.name = "";
        }
        this.videoModal.appendChild(minBtn);

        return minBtn;
    })();

    //关闭视频按钮
    closeVideo = (() => {
        const closeVideo = document.createElement("span");
        closeVideo.innerText = "×";
        closeVideo.style.display = "inline-block";
        closeVideo.style.border = "1px solid #ff0000";
        closeVideo.style.width = "20px";
        closeVideo.style.height = "20px";
        closeVideo.style.lineHeight = "20px";
        closeVideo.style.fontSize = "12px";
        closeVideo.style.borderRadius = "4px";
        closeVideo.style.color = "#ff0000";
        closeVideo.style.position = "absolute";
        closeVideo.style.right = "4px";
        closeVideo.style.top = "4px";
        closeVideo.style.cursor = "pointer";
        closeVideo.onclick = () => {
            this.minBtn.style.visibility = "hidden";
            this.maxBtn.style.visibility = "visible";
            this.videoModal.style.width = "300px";
            this.videoModal.style.height = "unset";
            this.videoModal.dataset.name = "";
            editor3DStore.setActiveMonitorId("");
        }
        this.videoModal.appendChild(closeVideo);
        return closeVideo;
    })();

    //视频dom
    video = (() => {
        const video = document.createElement("video");
        video.muted = true;
        video.autoplay = true;
        video.style.width = "100%";
        video.style.backgroundColor = "#556783";
        video.style.marginTop = "4px";
        this.videoModal.appendChild(video);
        return video;
    })();

    // 设置 dom 容器
    async setContainer(div: HTMLDivElement | null, license?: string) {

        if (utils.isProd) {
            var isvalid = await this.ck("/newmapserver/api/Permission/IsLegal?license=" + license);
            if (false == isvalid) {
                if (div) {
                    div.innerHTML = "无效许可,请联系软件开发商。";
                }
                return;
            }
        }

        // 保存许可
        this.license = license;

        await super.setContainer(div);

        if (div) {
            div.style.position = "relative";
            div.appendChild(this.videoModal);
            div.appendChild(this.infoModal.root);
        }

        //@ts-ignore
        threeUtils.removeEventListener("requestAnimationFrame", this.renderBindThis);
        //@ts-ignore
        utils.removeEventListener("requestAnimationFrame", this.renderBindThis);
        //@ts-ignore
        utils.addEventListener("requestAnimationFrame", this.renderBindThis);
    }

    // 停止渲染
    stopRender() {
        super.stopRender();
        //@ts-ignore
        utils.removeEventListener("requestAnimationFrame", this.renderBindThis);
    }

    //天空
    sky = (() => {
        const sky = new Sky();
        this.scene.add(sky);
        return sky;
    })();

    // 装饰地面
    ground = (() => {
        const ground = new Ground();
        ground.position.set(0, -0.01, 0);
        ground.scale.set(2000, 1, 2000);

        //three事件
        ground.userData = {
            cursor: "pointer",
            enableEvent: true
        };
        ground.addEventListener("leftclick", (e: any) => {

            const { state, addModelUrl, addModelInfo, addLabelId, inFloorId } = editor3DStore;

            if (state === "addModel" && addModelUrl) {
                const modelDatas = toJS(editor3DStore.modelDatas);
                editor3DStore.setModelDatas([
                    ...modelDatas,
                    {
                        id: generateUUID(),
                        name: addModelUrl,
                        url: addModelUrl,
                        floorId: inFloorId,
                        positionX: e.point.x,
                        positionY: e.point.y,
                        positionZ: e.point.z,
                        heading: 0,
                        pitch: 0,
                        roll: 0,
                        info: addModelInfo
                    }
                ]);

                // 添加标签数据
            } else if (state === "addLabel" && addLabelId) {
                editor3DStore.setLabelDataById(addLabelId, {
                    floorId: inFloorId,
                    positionX: e.point.x,  // x坐标
                    positionY: e.point.y,  // y坐标
                    positionZ: e.point.z,  // z坐标
                })

                // 添加监控
            } else if (state === "addMonitor") {
                editor3DStore.addMonitorData({
                    id: generateUUID(),
                    name: "",
                    floorId: inFloorId,
                    url: editor3DStore.monitorUrl,
                    positionX: e.point.x,  // x坐标
                    positionY: e.point.y,  // y坐标
                    positionZ: e.point.z,  // z坐标
                    heading: 0,
                    pitch: 0,
                    roll: 0,
                });
                //添加楼层
            } else if (state === "addFloor") {

            }
            editor3DStore.cancelAdd();
        });
        // 右键取消添加
        ground.addEventListener("rightclick", (e: any) => {
            const { state, addModelUrl, addLabelId } = editor3DStore;
            if (state === "addModel") {
                editor3DStore.cancelAdd();
            } else if (state === "addLabel" && addLabelId) {
                editor3DStore.deleteLabelDate(addLabelId);
            }
            editor3DStore.cancelAdd();
        });

        ground.addEventListener("mousemove", (e: any) => {
            const { state, addModelUrl, addLabelId, addFloorid, activeFloorId } = editor3DStore;
            if (state === "addModel") {
                this.addPreView.position.copy(e.point);
            } else if (state === "addLabel" && addLabelId) {
                editor3DStore.setLabelDataById(addLabelId, {
                    positionX: e.point.x,  // x坐标
                    positionY: e.point.y,  // y坐标
                    positionZ: e.point.z,  // z坐标
                })
            } else if (state === "addMonitor") {
                this.monitorGroup.addPervire.position.copy(e.point);

            } else if (state === "addFloor" && addFloorid) {
                editor3DStore.setFloorDataById(addFloorid, {
                    positionX: e.point.x,  // x坐标
                    positionY: e.point.y,  // y坐标
                    positionZ: e.point.z,  // z坐标
                });
            }
        });

        //根据状态控制鼠标事件
        autorun(() => {
            const { state } = editor3DStore;
            Object.assign(ground.userData, {
                enableEvent: ["addModel", "addLabel", "addMonitor", "addFloor"].includes(state),
            });
        });

        this.scene.add(ground);
        return ground;
    })();

    //高亮模型 或者楼层模型
    acticeMesh = (() => {
        const acticeMesh = new Group();

        acticeMesh.userData = {
            stroke_enable: true,//启用描边
            stroke_color: "#04fa12",//描边颜色
            stroke_width: 1,//描边厚度(像素)
        };

        autorun(() => {
            const { activeId, activeFloorId, inFloorId } = editor3DStore;
            //不渲染当前选中的模型
            const modelDatas = toJS(editor3DStore.modelDatas);
            const floorDatas = toJS(editor3DStore.floorDatas);

            //按照url 分组
            const urls = uniq(modelDatas.map(d => d.url)).concat(uniq(floorDatas.map(d => d.url)));

            //先全部隐藏
            acticeMesh.children.find(o => o.visible = false);

            // 遍历 url
            for (let url of urls) {
                if (!acticeMesh.children.some(o => o.name === url)) {
                    const model = new MeshBase(url);

                    //如果是楼层模型就可以双击进入楼层
                    if (floorDatas.some(f => f.url === url)) {

                        model.traverseMesh(m => {
                            m.userData = {
                                cursor: "pointer",
                                enableEvent: true,

                            };
                            m.addEventListener("dbclick", (e: any) => {
                                editor3DStore.setInFloorId(editor3DStore.activeFloorId);
                            });
                        });
                    }

                    model.name = url;
                    acticeMesh.add(model);
                }
            }

            const modelData = editor3DStore.getModelDataById(activeId);
            const floorData = editor3DStore.getFloorDataById(activeFloorId);

            acticeMesh.children.map(o => {
                if (modelData && o.name === modelData.url) {
                    o.position.set(
                        modelData.positionX,
                        modelData.positionY,
                        modelData.positionZ
                    );

                    o.rotation.set(
                        degToRad(modelData.heading),
                        degToRad(modelData.pitch),
                        degToRad(modelData.roll)
                    );

                    o.visible = true;
                } else if (floorData && o.name === floorData.url && inFloorId) {

                    o.position.set(
                        floorData.positionX,
                        floorData.positionY,
                        floorData.positionZ
                    );

                    o.rotation.set(
                        degToRad(floorData.heading),
                        degToRad(floorData.pitch),
                        degToRad(floorData.roll)
                    );

                    o.visible = true;

                } else {
                    o.visible = false;
                }
            });
        });

        this.scene.add(acticeMesh);
        return acticeMesh;
    })();

    //添加模型时的预览模型预览
    addPreView = (() => {
        const addPreView = new MeshBase("");
        addPreView.addEventListener("loaded", () => {
            addPreView.traverseMesh(mesh => {
                const { color, map, transparent, opacity, alphaTest, side } = mesh.material as MeshBasicMaterial;
                mesh.material = new MeshLambertMaterial({ color, map, transparent, opacity, alphaTest: 0.1, side });
                if (!mesh.geometry.getAttribute("normal")) {
                    mesh.geometry.computeVertexNormals();
                }
            });
        });

        autorun(() => {
            const { addModelUrl } = editor3DStore;
            if (addModelUrl) {
                addPreView.loadModel(addModelUrl);
                addPreView.position.set(Infinity, Infinity, Infinity);
                addPreView.visible = true;
            } else {
                addPreView.clear();
                addPreView.visible = false;
            }
        });

        this.scene.add(addPreView);
        return addPreView;
    })();

    // 模型组管理
    modelGroup = (() => {
        this.scene.add(modelGroup);
        return modelGroup;
    })();

    //标签文本
    labelGroup = (() => {
        const labelGroup = new LabelGroup();
        this.scene.add(labelGroup);
        return labelGroup;
    })();

    // 监控设备组
    monitorGroup = (() => {
        const monitorGroup = new MonitorGroup(this.video);
        this.scene.add(monitorGroup);
        return monitorGroup;
    })();

    // 加载地面模型
    groundMesh = (() => {
        const groundMesh = new MeshBase("");

        groundMesh.addEventListener("loaded", () => {
            const { state } = editor3DStore;

            groundMesh.traverseMesh(mesh => {

                //three事件
                mesh.userData = {
                    cursor: "pointer",
                    enableEvent: ["addModel", "addLabel", "addMonitor", "addFloor"].includes(state) && ["AM113_061_Aesculus_Glabra_Defintion1_372104", "AM113_061_Aesculus_Glabra_Defintion1_372087"].includes(mesh.name)
                };

                // 给地面模型设置 lamber 材质
                groundMesh.traverseMesh(mesh => {
                    const { color, map, transparent, opacity, alphaTest, side } = mesh.material as MeshBasicMaterial;
                    mesh.material = new MeshLambertMaterial({ color, map, transparent, opacity, alphaTest, side });
                    if (!mesh.geometry.getAttribute("normal")) {
                        mesh.geometry.computeVertexNormals();
                    }
                });

                mesh.addEventListener("leftclick", (e: any) => {

                    const { state, addModelUrl, addModelInfo, addLabelId, inFloorId } = editor3DStore;

                    if (state === "addModel" && addModelUrl) {
                        const modelDatas = toJS(editor3DStore.modelDatas);
                        editor3DStore.setModelDatas([
                            ...modelDatas,
                            {
                                id: generateUUID(),
                                name: addModelUrl,
                                url: addModelUrl,
                                floorId: inFloorId,
                                positionX: e.point.x,
                                positionY: e.point.y,
                                positionZ: e.point.z,
                                heading: 0,
                                pitch: 0,
                                roll: 0,
                                info: addModelInfo
                            }
                        ]);

                    } else if (state === "addLabel" && addLabelId) {
                        editor3DStore.setLabelDataById(addLabelId, {
                            floorId: inFloorId,
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                        })
                        editor3DStore.cancelAdd();
                    } else if (state === "addMonitor") {
                        editor3DStore.addMonitorData({
                            id: generateUUID(),
                            name: "",
                            floorId: inFloorId,
                            url: editor3DStore.monitorUrl,
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                            heading: 0,
                            pitch: 0,
                            roll: 0,
                        });
                    } else if (state === "addFloor") {
                        editor3DStore.cancelAdd();
                    }


                });
                // 右键取消添加
                mesh.addEventListener("rightclick", (e: any) => {
                    const { state, addModelUrl, addLabelId, addFloorid } = editor3DStore;
                    if (state === "addModel") {

                    } else if (state === "addLabel" && addLabelId) {
                        editor3DStore.deleteLabelDate(addLabelId);
                    } else if (state === "addFloor" && addFloorid) {
                        editor3DStore.deleteFloorById(addFloorid);
                    } else if (state === "addFloor" && addFloorid) {
                        editor3DStore.deleteFloorById(addFloorid);
                    }
                    editor3DStore.cancelAdd();
                });

                mesh.addEventListener("mousemove", (e: any) => {
                    const { state, addModelUrl, addLabelId, addFloorid } = editor3DStore;
                    if (state === "addModel") {
                        this.addPreView.position.copy(e.point);
                    } else if (state === "addLabel" && addLabelId) {
                        editor3DStore.setLabelDataById(addLabelId, {
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                        })
                    } else if (state === "addMonitor") {
                        this.monitorGroup.addPervire.position.copy(e.point);
                        //移动楼层
                    } else if (state === "addFloor") {
                        editor3DStore.setFloorDataById(addFloorid, {
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                        })
                    }
                });
            });
        })

        // 设置鼠标状态
        autorun(() => {
            const { state, inFloorId } = editor3DStore;
            groundMesh.traverseMesh(mesh => {
                Object.assign(mesh.userData, {
                    enableEvent: ["addModel", "addLabel", "addMonitor", "addFloor"].includes(state) && ["AM113_061_Aesculus_Glabra_Defintion1_372104", "AM113_061_Aesculus_Glabra_Defintion1_372087"].includes(mesh.name)
                });
            });
        });

        //加载地面模型
        autorun(() => {
            const { groundUrl } = editor3DStore;
            if (groundUrl) {
                groundMesh.loadModel(groundUrl);
            }
        });

        this.scene.add(groundMesh);
        return groundMesh;
    })();

    //楼层模型
    floorMeshGroup = (() => {
        this.scene.add(floorMeshGroup);
        return floorMeshGroup;
    })();



    // 获取当前相机的视角和方向
    GetCurrentCamera() {
        const { position, center } = this.getLookAt();

        return {
            position: {
                x: position.x,
                y: position.y,
                z: position.z,
            },
            center: {
                x: center.x,
                y: center.y,
                z: center.z,
            },
        }

    }

    // 设置相机位置
    async FlytoCamera(
        cameraParame?: {
            position: { x: number, y: number, z: number },
            center: { x: number, y: number, z: number },
            duration?: number | undefined
        }) {
        await new Promise(resolve => requestAnimationFrame(resolve));
        if (cameraParame) {
            const { position, center, duration } = cameraParame;
            this.setLookAt({
                position: new Vector3(position.x, position.y, position.z),
                center: new Vector3(center.x, center.y, center.z),
                duration
            });
        }
    }

    render() {
        this.pointLight.position.copy(this.perspectiveCamera.position);
        super.render();

        // 计算监控视频的位置
        const { activeMonitorId, showInfoModal, infoModalPosition } = editor3DStore;
        const monitorData = editor3DStore.getMonitorDataById(activeMonitorId);
        if (monitorData) {
            const { positionX, positionY, positionZ } = monitorData;
            const { clientHeight, clientWidth, dataset } = this.videoModal;
            const position = new Vector3(positionX, positionY, positionZ);
            const v2 = this.v3tov2(position);
            if (dataset.name === "max") {
                this.videoModal.style.left = `0px`;
                this.videoModal.style.top = `0px`;
            } else {
                this.videoModal.style.left = `${v2.x - (clientWidth / 2)}px`;
                this.videoModal.style.top = `${v2.y - clientHeight - 40}px`;
            }
        } else {
            this.videoModal.style.left = `100%`;
        }

        //模型的信息弹窗
        this.infoModal.setShow(showInfoModal);
        if (showInfoModal) {
            const v3 = new Vector3(infoModalPosition.x, infoModalPosition.y, infoModalPosition.z);
            const v2 = this.v3tov2(v3);
            this.infoModal.setPosition(v2.x, v2.y);
        }

    }

    // 提前预加载模型组里的实例对象
    dispose = autorun(() => {
        const { state, addModelUrl, addFloorUrl, inFloorId } = editor3DStore;
        if (state === "addModel" && addModelUrl) {
            this.modelGroup.getInstanceMeshGroupByUrl(addModelUrl);
        } else if (state === "addFloor" && addFloorUrl) {
            this.floorMeshGroup.getInstanceMeshGroupByUrl(addFloorUrl);
        }

        // 进入楼层时不显示周围的模型
        // this.sky.visible = !inFloorId;
        // 进入楼层时 地板不显示的
        this.groundMesh.visible = !inFloorId;
    });

    // 选中的模型的事件
    dispose2 = autorun(() => {
        const { activeId } = editor3DStore;
        const modelData = editor3DStore.getModelDataById(activeId);
        if (modelData) {
            this.infoModal.setTitle(modelData.name);
            this.infoModal.setContent(modelData.info);
        }
        this.dispatchEvent({
            type: "modelSelect",
            activeId
        } as never);
    });

    // 选中的监控的事件
    dispose1 = autorun(() => {
        const { activeMonitorId } = editor3DStore;
        const monitorData = editor3DStore.getMonitorDataById(activeMonitorId);
        if (monitorData) {
            this.videoTitle.innerText = (monitorData.name || "监控画面");
            this.monitorGroup.setUrl(monitorData.url);
        }

        this.dispatchEvent({
            type: "mobitorSelect",
            activeMonitorId
        } as never);
    });

    // 选中的楼层的事件
    dispose3 = autorun(() => {
        const { activeFloorId } = editor3DStore;
        this.dispatchEvent({
            type: "floorSelect",
            activeFloorId
        } as never);
    });

    // 进入楼层的事件
    dispose4 = autorun(() => {
        const { inFloorId } = editor3DStore;
        this.dispatchEvent({
            type: "inFloor",
            inFloorId
        } as never);
    });


}

const editor3D = window.editor3D = new Editor3D();

export default editor3D;